package net.flowas.genericcrud.service.impl;

import java.util.HashSet;
import java.util.Set;

import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonArrayBuilder;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;

import net.flowas.genericcrud.ExecuteContext;
import net.flowas.genericcrud.spi.ProcessListener;

public class ValidationProcessListener implements ProcessListener {

	private ValidatorFactory validatorFactory;
	
	private Validator validator; 
	
	public void setValidatorFactory(ValidatorFactory validatorFactory) {
	    this.validatorFactory = validatorFactory;
	  }
	
	public void setValidator(Validator validator) {
	    this.validator = validator;
	  }
	
	public void init() {
		if (validatorFactory == null) {
		      validatorFactory = Validation.buildDefaultValidatorFactory();
		}
		validator = validatorFactory.getValidator();
	}
	
	@Override
	public void process(ExecuteContext req) {
		Object entity = req.getQueryOption().getExample();
		validate(entity);
	}
	private void validate(Object entity) {
	    try {
	      validateMember(entity);
	    } catch (ConstraintViolationException ce) {
	      // Handle bean validation issues
	      Response resp = Response.status(Response.Status.BAD_REQUEST)
	          .entity(createViolationResponse(ce.getConstraintViolations())).build();
	      throw new WebApplicationException(resp);
	    } catch (ValidationException e) {
	      // Handle the unique constrain violation
	      JsonArrayBuilder values = Json.createArrayBuilder();
	      values.add(Json.createObjectBuilder().add("conflict", e.getMessage()));
	      Response resp = Response.status(Response.Status.CONFLICT).entity(values.build()).build();
	      throw new WebApplicationException(resp);
	    } catch (Exception e) {
	      JsonArrayBuilder values = Json.createArrayBuilder();
	      values.add(Json.createObjectBuilder().add("error", e.getMessage()));
	      Response resp = Response.status(Response.Status.BAD_REQUEST).entity(values.build()).build();
	      throw new WebApplicationException(resp);
	    }
	  }

	  private JsonArray createViolationResponse(Set<ConstraintViolation<?>> violations) {
	    // Map<String, String> responseObj = new HashMap<String, String>();
	    JsonArrayBuilder values = Json.createArrayBuilder();
	    for (ConstraintViolation<?> violation : violations) {
	      // responseObj.put(violation.getPropertyPath().toString(), violation.getMessage());
	      values.add(Json.createObjectBuilder().add(violation.getPropertyPath().toString(),
	          violation.getMessage()));
	    }
	    return values.build();
	  }
	  private void validateMember(Object member) {
		    // Create a bean validator and check for issues.
		    Set<ConstraintViolation<Object>> violations = validator.validate(member);

		    if (!violations.isEmpty()) {
		      throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>(violations));
		    }

		    // Check the uniqueness of the email address
		    // if ("hello".equals(member.getName())) {
		    // throw new ValidationException("Unique Email Violation");
		    // }
		  }
}
